
package DB;

import Data.*;

import java.sql.PreparedStatement;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

/**
 * Mapper Klasse, die TypeOfAircraft Objekte auf eine relationale DB abbildet.
 * Diese Klasse erlaubt zahlreiche Operationen, um einen typeOfAircraft zu verwalten, z.B. eine LöschOperation oder eine EinfügeOperation
 * @author Stefan Fellner
 */
public class TypeOfAircraftMapper {

    /*
     * Um sicher zu stellen, dass nur eine Instanz von der Klasse erzeugt wird, wird die Singleton Pattern angewendet.
     * Durch das Schlüsselwort static wird dies erreicht.
     * Quelle: http://www.theserverside.de/singleton-pattern-in-java/
     */
    private static TypeOfAircraftMapper typeOfAircraftMapper = null;

    private ConnectionManager manager;

    //mittels protected wird sichergestellt, dass mit new keine neuen Instanzen erstellt werden können. Sobals ein Mapper Objekt erstellt wird, wird eine connection aufgebaut
    protected TypeOfAircraftMapper(){
        manager = ConnectionManager.getInstance();

    }

    /* Eine Instanz dieser Klasse sollte immer mittels der folgenden Methode erstellt werden.
     * Dadurch wird sicher gestellt, dass nur eine Instanz von dieser Klasse vorhanden ist.
     * @return DAS <code>TypeOfAircraftMapper</code> Objekt
     * @see typeOfAircraftMapper
     */
    public static TypeOfAircraftMapper getInstance(){

        if(typeOfAircraftMapper == null){
            typeOfAircraftMapper = new TypeOfAircraftMapper();
        }

        return typeOfAircraftMapper;

    }


//Es folgen nun alle methoden, die die Tabelle typeOfAircraft verändern können, bzw. deren inhalte abrufen können


    /* Eine TypeOfAircraft in die Datenbank einfügen.
     * @param das TypeOfAircraft Objekt welches hinzugefügt werden soll
     * @return das eingefügte Objekt.
     * Diese Methode wird den Primärschlüssel automatisch erzeugt. Der größte vorhandene Schlüssel wird um 1 erhöht.
     */
    public TypeOfAircraft insert (TypeOfAircraft type){
        //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();



        //es wird sichergestellt, dass der name des einzufügenden TypeOfAircraft noch nicht in der Datenbank vorhanden ist.
        try{
            if(findByName(type.getCraftTypeName()) != null){
                //wenn schon ein TypeOfAircraft mit dem gleichen Namen existiert, wird null zurückgegeben und die insert operation findet für das übergebene Objekt nicht statt.
               return null;
            }
        }
        catch(RemoteException ex){
            ex.printStackTrace();
        }



        try{
            // Leeres SQL-Statement (JDBC) anlegen
            Statement stmt = con.createStatement();

            /*
             * Zunächst schauen wir nach, welches der momentan größte
             * Primärschlüsselwert eines TypeOfAircrafts ist.
             */
            ResultSet maxId = stmt.executeQuery("SELECT MAX(craftTypeId) AS maxId " +"FROM typeOfAircraft ");

            //die ergebnis Tabelle muss eine maxId zurückgeben.
            if(maxId.next()){
                try{

                 //Das einzufügende Objekt a erhält den neu errechneten Primärschlüssel
                 type.setCraftTypeId(maxId.getInt("maxId") + 1);

                    stmt = con.createStatement();

                     /**
                     * Jetzt erst erfolgt die tatsächliche Einfügeoperation.
                     * Die Attribute des typeOfAircrafts, werden aus dem übergebenen typeOfAircraft mit den getter methoden geholt. Zudem wird die ID der sitzplatzstruktur aus dem TypeOfAircraft geholt.
                     */
                    stmt.executeUpdate("INSERT INTO typeOfAircraft (craftTypeId, craftTypeName, seatId)" +
                            "VALUES (" +"'"+
                            type.getCraftTypeId()+"'"+", "+"'"+
                            type.getCraftTypeName()+"'"+", "+"'"+
                            type.getSeatId()+"'"+")");
                }
                //falls der Zugriff auf die Klasse TypeOfAircraft fehlschlägt.
                catch(RemoteException ex){
                    ex.printStackTrace();
                }
            }

        }
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }
        //dieses return ist notwendig, da sich die ID des TypeOfAircrafts im Laufe der Methode verändert.
        return type;
    }







    /* Ein TypeOfAircraft updaten, d.h. die Attribute des TypeOfAircraftes aktualisieren
     * @param das TypeOfAircraft Objekt den aktualisierten Attributen
     */
     public TypeOfAircraft update (TypeOfAircraft type){

         //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();


        //es wird sichergestellt, dass der name des einzufügenden TypeOfAircraft noch nicht in der Datenbank vorhanden ist.
        try{
            if(findByName(type.getCraftTypeName()) != null){
                //wenn schon ein TypeOfAircraft mit dem gleichen Namen existiert und dies nicht der Type mit der übergeben ID ist, wird null zurückgegeben und die insert operation findet für das übergebene Objekt nicht statt.
                if(findByName(type.getCraftTypeName()).getCraftTypeId() != type.getCraftTypeId()){
                    return null;
                }
            }
        }
        catch(RemoteException ex){
            ex.printStackTrace();
        }



        try{
                //sql statement
            PreparedStatement stmt = con.prepareStatement("UPDATE typeOfAircraft "+
                    "SET craftTypeName= "+"'"+type.getCraftTypeName()+"'"+", "+
                    "seatId ="+"'"+type.getSeatId()+"'"+
                    " WHERE craftTypeId = "+"'"+type.getCraftTypeId()+"'");
            //dieser integer beinhaltet die anzahl der veränderten datensätze nach einer bestimmten UPDATE operation
            int anzahlVeraenderterDatensaetze = stmt.executeUpdate();
            }
            //falls der Zugriff auf die Klasse TypeOfAircraft scheitert
            catch(RemoteException ex){
                ex.printStackTrace();
            }

        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }
        //Dadurch wird sichergestellt, dass die update operation erfolgreich war, ansonsten würde NULL zurückgegeben werden (Name schon vorhanden)
        return type;

     }


     /* Ein bestimmten TypeOfAircraft in der Datenbank löschen
      * @param der zu löschende TypeOfAircraft
      */
     public void delete (TypeOfAircraft type){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM typeOfAircraft "+
                               "WHERE craftTypeId = "+"'"+type.getCraftTypeId()+"'");
        }
        //falls der Zugriff auf die Klasse TypeOfAircraft fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /* Alle TypeOfAircraft in der Datenbank löschen, die die übergebene seatId assoziiert haben
      * @param der zu löschende TypeOfAircraft
      */
     public void deleteAllTypesAssociatedWithSeatId (int seatId){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM typeOfAircraft "+
                               "WHERE seatId = "+"'"+seatId+"'");
        }

        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /*Alle TypeOfAircrafts, die in der Tabelle vorhanden sind werden zurückgegeben.
      *Die zurückgegebenen Objekte haben neben ihren gängigen Attributen auch die ID der Sitzplatzstruktur!
      *@return Vector mit allen TypeOfAircrafts
      */
     public Vector<TypeOfAircraft> findAll(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle TypeOfAircrafts Objekte festhält
         Vector<TypeOfAircraft> allTypesOfAircrafts = new Vector<TypeOfAircraft>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allTypesOfAircraftsQueryResult = stmt.executeQuery("SELECT craftTypeId, craftTypeName, seatId FROM typeOfAircraft "+"ORDER BY craftTypeId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues TypeOfAircraft Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allTypesOfAircraftsQueryResult.next()){
                 //ein neues TypeOfAircraft Objekt anlegen, welches in den ergebnisvektor aufgenommen wird
                 TypeOfAircraft type = new TypeOfAircraftImpl(allTypesOfAircraftsQueryResult.getString("craftTypeName"), allTypesOfAircraftsQueryResult.getInt("seatId"));
                 type.setCraftTypeId(allTypesOfAircraftsQueryResult.getInt("craftTypeId"));

                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allTypesOfAircrafts.addElement(type);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen TypesOfAircrafts aus der Tabelle wird zurückgegeben
         return allTypesOfAircrafts;
     }


     /*Alle TypeOfAircrafts, die die übergebene seatId haben.
      *Die zurückgegebenen Objekte haben neben ihren gängigen Attributen auch die ID der Sitzplatzstruktur!
      *@return Vector mit allen zutreffenden TypeOfAircrafts
      */
     public Vector<TypeOfAircraft> findAllTypesAssociatedWithSeatId(int seatId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle TypeOfAircrafts Objekte festhält
         Vector<TypeOfAircraft> allTypesOfAircrafts = new Vector<TypeOfAircraft>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes Tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allTypesOfAircraftsQueryResult = stmt.executeQuery("SELECT craftTypeId, craftTypeName, seatId FROM typeOfAircraft "+
                                                                            "WHERE seatId = "+"'"+seatId+"'"+"ORDER BY craftTypeId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues TypeOfAircraft Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allTypesOfAircraftsQueryResult.next()){
                 //ein neues TypeOfAircraft Objekt anlegen, welches in den ergebnisvektor aufgenommen wird
                 TypeOfAircraft type = new TypeOfAircraftImpl(allTypesOfAircraftsQueryResult.getString("craftTypeName"), allTypesOfAircraftsQueryResult.getInt("seatId"));
                 type.setCraftTypeId(allTypesOfAircraftsQueryResult.getInt("craftTypeId"));

                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allTypesOfAircrafts.addElement(type);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen TypesOfAircrafts aus der Tabelle wird zurückgegeben
         return allTypesOfAircrafts;
     }



     /* TypeOfAircrafts mit einem bestimmten Namen zurück geben. Es wird nur ein TypeOfAircraft zurückgegeben, da nur ein Type mit diesem Namen existiert.
      * @param den typeOfAircraftNamen
      * @return TypeOfAircraft Objekt, dass dem übergebenen Namen entspricht.
      */

     public TypeOfAircraft findByName(String craftTypeName){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredTypeOfAircraft = stmt.executeQuery("SELECT craftTypeId, craftTypeName, seatId FROM typeOfAircraft "+
                                                         "WHERE craftTypeName = "+"'"+craftTypeName+"'");

             //prüfen, ob es ein TypeOfAircraft mit dem übergebenen Namen gibt. Es kann max. 1 geben.
             if(desiredTypeOfAircraft.next()){
                 //ein neues TypeOfAircraft Objekt anlegen
                 TypeOfAircraft type = new TypeOfAircraftImpl(desiredTypeOfAircraft.getString("craftTypeName"), desiredTypeOfAircraft.getInt("seatId"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 type.setCraftTypeId(desiredTypeOfAircraft.getInt("craftTypeId"));
                 //das gerade erstellte Objekt wird zurückgegeben
                 return type;

            }

         }
         //falls der Zugriff auf die Klasse Aircraft fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //falls kein TypeOfAircraft in der DB vorhanden ist und es keine Fehlermeldung gab, wird NULL zurückgegeben
         return null;
     }



     /*Einen bestimmten TypeOfAircraft abhängig von der ID finden
      *
      * @param die ID des TypeOfAircrafts als integer
      * @return das gesuchte TypeOfAircraft Objekt
      */

     public TypeOfAircraft findById(int typeOfAircraftId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredTypeOfAircraft = stmt.executeQuery("SELECT craftTypeId, craftTypeName, seatId FROM typeOfAircraft "+
                                                         "WHERE craftTypeId = "+"'"+typeOfAircraftId+"'");

             //prüfen, ob es ein TypeOfAircraft mit dem übergebenen Namen gibt. Es kann max. 1 geben.
             if(desiredTypeOfAircraft.next()){
                 //ein neues TypeOfAircraft Objekt anlegen
                 TypeOfAircraft type = new TypeOfAircraftImpl(desiredTypeOfAircraft.getString("craftTypeName"), desiredTypeOfAircraft.getInt("seatId"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter Methode gesetzt.
                 type.setCraftTypeId(desiredTypeOfAircraft.getInt("craftTypeId"));
                 //das gerade erstellte Objekt wird zurückgegeben
                 return type;

            }

         }
         //falls der Zugriff auf die Klasse Aircraft fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //falls kein TypeOfAircraft in der DB vorhanden ist und es keine Fehlermeldung gab, wird NULL zurückgegeben
         return null;
     }


     /**
	 * Auslesen des zugehörigen <code>Seat</code>-Objekts zu einem gegebenen
	 * TypeOfAircraft.
	 *
	 * @param der TypeOfAircraft, wessen Seatstructure erfahren werden soll.
	 * @return ein Objekt, welches den gesuchten Seat darstellt
	 */
	public Seat getSeatOfTypeOfAircraft(TypeOfAircraft craftType) {
		try {
			/*
			 * Die SeatMapper Klasse wird aufgerufen bzw. deren findById Methode.
                         * Dadurch bekommen wir das gesuchte Seat Objekt zurück
			 */
                         return SeatMapper.getInstance().findById(craftType.getSeatId());

		}
		catch (RemoteException e) {
			e.printStackTrace();
			// Wenn der Zugriff auf die SeatMapper fehlschlägt, dann geben wir null zurück.
			return null;
		}
	}

}
